(module top (lib "eopl.ss" "eopl")
  
  ;; top level module.  Loads all required pieces and runs the test
  ;; suite.  
  
  (require "drscheme-init.scm")
  (require "data-structures.scm")       ; for expval constructors
  (require "lang.scm")                  ; for scan&parse
  (require "interp.scm")                ; for value-of-program

  ;; choose one of the following test suites

  ;;(require "test-suite.scm")        ; ordinary test suite        
  ;;(require "tests-book.scm")     ; examples from book/lecture notes   
  (require "tests-mp8.scm")     ; Tests  for MP8


  ;; for type checker
  (require "check-modules.scm")
  (require "equal-types.scm")
  
  (provide (all-defined))
  (provide (all-from "interp.scm"))
  (provide (all-from "lang.scm"))
  
  ;;;;;;;;;;;;;;;; data definitions ;;;;;;;;;;;;;;;;

  ;; test-item ::= (test-name program correct-ans)
  ;; test-list is a list of test-items.

  ;;;;;;;;;;;;;;;; shared infrastructure ;;;;;;;;;;;;;;;;

  (define show-error-messages? (make-parameter #f))

  ;; default settings
  (stop-after-first-error #t)
  (run-quietly #t)


  ;; make-tester : (test-item -> ans) * (val*val -> bool)
  ;;               -> (test-item -> bool[&msgs])
  (define make-tester
    (lambda (fn equal-answer?)
      (lambda (test-item)
        (let ((test-name (list-ref test-item 0))
              (pgm       (list-ref test-item 1))
              (correct-ans (list-ref test-item 2)))
          (eopl:printf "~s... " test-name)  
          (let ((outcome
                  (run-experiment fn (list pgm) correct-ans equal-answer?)))
            (if (car outcome)
              ;; run-experiment thinks this one passed
              (if (eqv? correct-ans 'error)
                ;; this one should throw an exception
                (if (show-error-messages?)
                  (eopl:printf
                      "Correctly reported error.  Error reported was:~%~s~%"
                      (cdr outcome))
                  (eopl:printf "passed ~%"))
                ;; right answer
                (eopl:printf "passed ~%"))
              ;; run-experiment thinks this one failed.  Record the
              ;; name of this test.
              (begin
                (eopl:printf
                  "FAILED~%correct outcome: ~s~%actual outcome: ~s~%~%"
                correct-ans (cdr outcome))
                ;; save the name of the last test that failed
                (set! last-failure test-name)))
            ;; in any case, return succ/failure.
            (car outcome))))))


  ;; do the tests on all of the test items in test-list
  (define test-all
    (lambda (run test-list equal-answer?)
      (let ((tester (make-tester run equal-answer?)))
        (let loop ((tests test-list))
          (if (null? tests) #t
            (let ((first-outcome (tester (car tests))))
              (if (or
                    first-outcome
                    (not (stop-after-first-error)))
                (loop (cdr tests))
                #f)))))))

  (define last-failure '())

  (define test-one
    (lambda (fn test-name test-list)
      (let ((the-test (assoc test-name test-list)))
        (cond
          (the-test
            => (lambda (test)
                   (eopl:printf "~s: " test-name)  
                   (eopl:printf "~a~%" (cadr test))
                   (fn (cadr test))))
          (else (eopl:error 'test-one "no such test: ~s" 
                  test-name))))))

  (define repeat-last-test
    (lambda (fn test-list)
      (test-one fn last-failure test-list)))

  ;;;;;;;;;;;;;;;; parsing ;;;;;;;;;;;;;;;;
  
  ;; parsing requires a slightly different structure

  (define parse-all
    (lambda ()
      (for-each
        (lambda (test-item)
          (let ((test-name (list-ref test-item 0))
                (pgm       (list-ref test-item 1)))
            (eopl:printf "~s... " test-name)               
            (let ((outcome
                    (apply-safely scan&parse (list pgm))))
              (if (car outcome)
                (eopl:printf "passed ~%")
                (begin
                  (eopl:printf "failed ~%")
                  (set! last-failure test-name)
                  (if (stop-after-first-error)
                    (eopl:error test-name "incorrect outcome detected")))))))
        tests-for-parse)))

  (define parse-one
    (lambda (test-name)
      (test-one scan&parse test-name tests-for-parse)))

  (define parse-last
    (lambda ()
      (repeat-last-test scan&parse tests-for-parse)))


  ;;;;;;;;;;;;;;;; evaluation ;;;;;;;;;;;;;;;;

  ;; run : string -> expval
  
  (define run
    (lambda (string)
      (value-of-program (scan&parse string))))

  (define run-all 
    (lambda ()
      (test-all run tests-for-run equal-answer?)))

  (define run-one
    (lambda (test-name)
      (test-one run test-name tests-for-run)))

  (define run-last
    (lambda ()
      (repeat-last-test run tests-for-run)))
  
  (define equal-answer?
    (lambda (ans correct-ans)
      (equal? ans (sloppy->expval correct-ans))))
  
  (define sloppy->expval 
    (lambda (sloppy-val)
      (cond
        ((number? sloppy-val) (num-val sloppy-val))
        ((boolean? sloppy-val) (bool-val sloppy-val))
        (else
          (eopl:error 'sloppy->expval 
            "Can't convert sloppy value to expval: ~s"
            sloppy-val)))))
  

  ;;;;;;;;;;;;;;;; type checking ;;;;;;;;;;;;;;;;
  
  ;; check : string -> external-type
  
  (define check
    (lambda (string)
      (type-to-external-form
        (type-of-program (scan&parse string)))))

  ;; check-all : () -> unspecified
  ;; checks all the tests in test-list, comparing the results with
  ;; equal-types?
  
  (define check-all 
    (lambda ()
      (test-all check tests-for-check equal-types?)))
 
  ;; check-one : symbol -> expval
  
  (define check-one
    (lambda (test-name)
      (test-one check test-name tests-for-check)))

  (define check-last
    (lambda ()
      (repeat-last-test check tests-for-check)))


  ;;;;;;;;;;;;;;;; top level

  ;; do one of the following, but probably not inside the module:
   ;;(parse-all)
   (run-all)
   (check-all)
  
  (set! last-failure 'mybool-1)
  
  
   
  )




